home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
TeX 1995 July
/
TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO
/
dviware
/
umddvi
/
dev
/
verser2.c
< prev
Wrap
C/C++ Source or Header
|
1990-10-01
|
19KB
|
759 lines
/*
* Copyright (c) 1987 University of Maryland Department of Computer Science.
* All rights reserved. Permission to copy for any purpose is hereby granted
* so long as this copyright notice remains intact.
*/
#ifndef lint
static char rcsid[] = "$Header: verser2.c,v 2.5 87/06/16 17:15:19 chris Exp $";
#endif
/*
* Verser2 -- Second half of DVI to Versatec driver
*
* Reads pre-sorted pages as put out by verser1, and shovels bitmaps
* out to the Versatec as fast as possible. Warning: there is some
* inline assembly code used in inner loops, where the C compiler
* produced particuarly poor code.
*
* We use a technique known as a `band buffer', where we keep track
* of what has yet to be written to the Versatec in a buffer that
* represents a `band' across the current page, analagous to a magnifying
* bar across the page. Only the region in the band can be written,
* and the band moves only downward; this is why verser1 must sort
* each page, at least by y coordinate. This also implies that the
* `tallest' object we can write is the same height as the band. This
* is a problem for large characters. For these there is some (as yet
* unimplemented) code that will ask for a `part' of each character
* to be drawn into the band. The character would then be repeated
* with a request for the next part when the band has moved down to
* just below the bottom of the previous part. Rules are also broken
* up as appropriate (and that code *is* implemented).
*
* Another important point is that the band buffer is treated as a
* `cylinder' rather than a `strip': we write bits onto the cylinder,
* then roll it forward over the page, moving the bits off the cylinder
* and onto the paper, leaving that part of the cylinder clean, ready
* for more bits. The variable `CurRow' points at the current row
* in the buffer/on the cylinder, and `FirstRow' and `LastRow' bound
* the `dirty' part of the cylinder. Modular arithmetic suffices to
* change linear to cylindrical.
*
* Whenever CurRow is more than MIN_OUT rows ahead of FirstRow, we
* write out that much of the cylinder, cleaning it. This keeps the
* paper moving, lest the developer soak one spot and produce `streaky'
* output.
*
* Yet another point of note is that because the band always moves
* `down' on the page, we need only a positive offset from the current
* row to move to a new row. This means (among other things) that we
* can use negative offsets for special purposes.
*/
#include <errno.h>
#include <setjmp.h>
#include <stdio.h>
#include <sys/vcmd.h>
#ifdef ACCOUNT_FILE
#include <pwd.h>
#endif ACCOUNT_FILE
#include "types.h"
#include "conv.h"
#include "fio.h"
#include "font.h"
#include "verser.h"
#define SPEED_HACK
char *ProgName;
extern int errno;
extern char *optarg;
extern int optind;
/* Globals */
jmp_buf failbuf; /* in case of Versatec write() problems */
struct font *Fonts[NFONTS]; /* the fonts */
char TeXFontDesc[256]; /* getenv("TEXFONTDESC") from verser1 */
int RasterOrientation; /* ROT_NORM or ROT_RIGHT, based on HFlag */
int DFlag; /* -d => output discarded */
int HFlag; /* -h => horizontal (rotated bitmaps) */
int SFlag; /* -s => silent processing */
int TFlag; /* -t => output to tape */
int Debug; /* -D => debug flag */
char VBuffer[ROWS][COLUMNS]; /* Versatec band buffer */
int CurRow; /* current row in buffer */
int CurCol; /* current column in buffer */
int FirstRow; /* the first row used */
int LastRow; /* the last row used */
int NLines; /* counts lines; used for pagefeeds */
int Pages; /* counts pages; for accounting */
int vp; /* Versatec file descriptor */
int pltmd[] = {VPLOT, 0, 0};/* print & plot mode, for ioctl */
int prtmd[] = {VPRINT, 0, 0};
#ifdef ACCOUNT_FILE
struct passwd *getpwuid();
#endif ACCOUNT_FILE
/*
* RowsBetween tells how many rows (in cylindrical arithmetic) there
* are between the first position and the second. If the second value
* is less than the first value, add ROWS to do the appropriate modular
* arithmetic. We cannot use `%' as C `%' is machine-dependent with
* respect to negative values.
*/
#define RowsBetween(f, n) ((n) >= (f) ? (n) - (f) : (n) - (f) + ROWS)
/*
* This is it... on your marks ... get set ... main!
*/
main(argc, argv)
int argc;
register char **argv;
{
register int c;
register char *s;
int dpi, usermag, num, denom, dvimag;
int VFlag = 0;
#ifdef ACCOUNT_FILE
int acct_fd;
acct_fd = open(ACCOUNT_FILE, 1);
(void) setuid(getuid());
#endif ACCOUNT_FILE
ProgName = *argv;
if (setjmp(failbuf)) { /* still have to do accounting */
#ifdef ACCOUNT_FILE
/*
* Kind of strange to charge for a printout that failed
* because we ran out of paper, but that was the way they
* wanted it....
*/
if (NLines)
Pages++;/* count the partial page */
DoAccount(acct_fd);
#endif ACCOUNT_FILE
exit(1);
/* NOTREACHED */
}
while ((c = getopt(argc, argv, "dstv:D")) != EOF) {
switch (c) {
case 'd': /* output to /dev/null */
DFlag++;
break;
case 's': /* silent processing except for errors */
SFlag++;
break;
case 't': /* output to tape (not implemented) */
TFlag++;
error(0, 0, "tape option not yet implemented");
break;
case 'v': /* Versatec already open as fd <n> */
VFlag++;
vp = atoi(optarg);
break;
case 'D':
Debug++;
break;
case '?':
fprintf(stderr, "Usage: %s [-d] [-s] [-t] [file]\n",
ProgName);
exit(1);
}
}
if (optind < argc)
if (freopen(argv[optind], "r", stdin) == NULL)
error(1, 0, "can't open %s", argv[optind]);
HFlag = getchar();
if ((HFlag >> 1) != VERSION)
error(1, 0, "input file is not version %d", VERSION);
HFlag &= 1;
RasterOrientation = HFlag ? ROT_RIGHT : ROT_NORM;
s = TeXFontDesc;
c = GetLong(stdin);
while (--c >= 0)
*s++ = getchar();
if (feof(stdin))
(void) GetByte(stdin); /* let GetByte do error */
*s = 0;
dpi = GetLong(stdin);
usermag = GetLong(stdin);
num = GetLong(stdin);
denom = GetLong(stdin);
dvimag = GetLong(stdin);
SetConversion(dpi, usermag, num, denom, dvimag);
fontinit(*TeXFontDesc ? TeXFontDesc : (char *) NULL);
ReadFonts();
if (DFlag) {
(void) fprintf(stderr, "Output will be discarded\n");
(void) fflush(stderr);
vp = open("/dev/null", 1);
} else {
if (!VFlag) {
vp = open(VERSATEC_FILE, 1);
if (vp < 0) {
if (errno == ENXIO)
error(1, 0, "\
can't open versatec---already in use");
if (errno == EIO)
error(1, 0, "\
can't open versatec---device offline");
error(1, errno, "can't open %s",
VERSATEC_FILE);
}
}
ioctl(vp, VSETSTATE, pltmd);
}
if (!HFlag)
CutMarks(); /* initial cut marks */
ReadInput();
FormFeed(0); /* end up in print mode */
if (!SFlag)
(void) putc('\n', stderr);
#ifdef ACCOUNT_FILE
DoAccounting(acct_fd);
#endif ACCOUNT_FILE
exit(0);
}
#ifdef ACCOUNT_FILE
/*
* Accounting is done by writing the program name ("tex"), the user name,
* and the number of pages at the end of the file. (The program name is
* for statistics.)
*/
DoAccounting(fd)
int fd;
{
register struct passwd *p;
char buf[128];
if (fd < 0 || (p = getpwuid(getuid())) == 0)
return;
/*
* The '+ 2' is because there is an extra page at the end, and
* because the Versatec does pagefeeds when it is opened.
*/
(void) sprintf(buf, "tex %s %d\n", p->pw_name, Pages + 2);
(void) lseek(fd, 0L, 2);
(void) write(fd, buf, strlen(buf));
}
#endif ACCOUNT_FILE
/*
* Read the font definitions.
*
* Anti-streak hack: get the rasters ahead of time, #ifdef SPEED_HACK.
*/
ReadFonts()
{
register struct font *f, **fp;
register int c;
register char *s;
#ifdef SPEED_HACK
register struct glyph *g;
#endif
i32 mag, dsize;
char *fname;
char nm[512];
if (!SFlag)
(void) fprintf(stderr, "[fonts:\n");
fp = Fonts;
while (GetByte(stdin) == 1) {
(void) GetLong(stdin); /* checksum */
mag = GetLong(stdin); /* magfactor */
dsize = GetLong(stdin); /* design size */
c = GetLong(stdin);
s = nm;
while (--c >= 0)
*s++ = getchar();
if (feof(stdin))
(void) GetByte(stdin); /* let GetByte do error */
*s = 0;
f = GetFont(nm, mag, dsize, "versatec", &fname);
if (f == NULL) {
GripeCannotGetFont(nm, mag, dsize, "versatec", fname);
exit(1);
/* NOTREACHED */
}
if (Debug) {
(void) fprintf(stderr, "[%s -> %s]\n",
Font_TeXName(f), fname);
(void) fflush(stderr);
}
if (!SFlag) {
register char *t = fname;
s = fname;
while (*s)
if (*s++ == '/' && *s)
t = s;
(void) fprintf(stderr, " %s\n", t);
}
#ifdef SPEED_HACK
for (c = 0; c < 128; c++) {
g = GLYPH(f, c);
if (GVALID(g))
(void) RASTER(g, f, RasterOrientation);
}
#endif
*fp++ = f;
}
if (!SFlag)
(void) fprintf(stderr, "]\n");
}
/*
* Read the input stream, decode it, and put character rasters or rules at
* the positions given.
*/
ReadInput()
{
register int yx, fcp, height;
/*
* Loop forever. I had a `for (;;)' but everything crept off the
* right side of the screen.
*/
next:
fGetLong(stdin, yx); /* position */
fGetLong(stdin, fcp); /* character, most likely */
if (feof(stdin))
return; /* done */
/*
* A `position' of -1 indicates either a rule or an end of page.
* Anything else is a character.
*/
if (yx != -1) { /* place character */
register struct glyph *g;
register struct font *f;
register int fnum;
/*
* Any delta-y required is stored in the upper 16 bits of yx.
*/
if ((height = yx >> 16) != 0)
MoveDown(height);
/*
* Extract the x, font, char, and part info into CurCol,
* fnum, yx, and fcp.
*/
CurCol = yx & 0xffff;
fnum = fcp >> FONTSHIFT;
yx = (fcp >> CHARSHIFT) & CHARMASK;
fcp = fcp & PARTMASK;
f = Fonts[fnum]; /* trusting */
g = GLYPH(f, yx);
/*
* In case this character does not fit, write
* out the used part of the band. It had better
* fit afterward....
*/
height = g->g_height;
if (height >= ROWS - RowsBetween(FirstRow, CurRow))
DumpTopOfBand();
if (fcp) /* cannot handle these yet */
error(0, 0, "\
part code not implemented; skipping char %d in %s",
yx, f->f_path);
else if (HASRASTER(g)) {
#ifdef SPEED_HACK
/* XXX, but saves time */
VWriteChar(g->g_raster, height, g->g_width);
#else
VWriteChar(RASTER(g, f, RasterOrientation),
height, g->g_width);
#endif
}
/* dump if we can do at least MIN_OUT rows */
if (RowsBetween(FirstRow, CurRow) > MIN_OUT)
DumpTopOfBand();
goto next; /* done with character */
}
/*
* If the `character' is negative, we need to move down first,
* possibly because this is an end-of-page. If this is not the
* end of the page, it must be a rule.
*/
if (fcp < 0) { /* move down */
yx = -fcp;
fGetLong(stdin, fcp); /* junk */
fGetLong(stdin, fcp);
if (fcp == 0) { /* end page */
/* dump entire band */
WriteBuf(&VBuffer[0][0], FirstRow, LastRow, 1);
CurRow = LastRow = FirstRow;
if (!HFlag) {
WriteBlanks(yx - NLines);
CutMarks();
} else
FormFeed(1);
if (!SFlag)
(void) fprintf(stderr, ".");
NLines = 0;
Pages++;
goto next; /* all done */
}
MoveDown(yx); /* must be a rule; move down by yx rows */
}
/*
* At this point we have a rule to put at the current
* position, CurRow.
*/
height = (fcp & 0xff00) >> 8;
/* make sure it fits */
if (height >= ROWS - RowsBetween(FirstRow, CurRow))
DumpTopOfBand();
VWriteRule(fcp);
goto next; /* done with rule */
}
/*
* Write the given raster for the given character.
*
* Basically, the task is to move bits from the raster to the Versatec
* buffer. However, because the character being plotted can be on an
* arbitrary bit boundary, things are not as simple as we might like.
* The solution used here is to shift each raster value right, OR it
* into the buffer, then (at the next location) OR in the bits that
* `fell off the right edge'.
*/
VWriteChar(rastp, height, width)
char *rastp; /* raster pointer */
int height, width; /* height & width of char */
{
register char *bp; /* Versatec buffer pointer [r11] */
register char *rp; /* raster pointer [r10] */
register int rshift; /* right shift index [r9] */
register int lshift; /* left shift index [r8] */
register int j; /* width loop downcounter */
register int o; /* offset to next row in buffer */
int row; /* current row in buffer */
int col; /* column in buffer of left edge */
int i; /* height loop downcounter */
int w; /* raster width (bytes) */
if ((rp = rastp) == NULL)
return; /* an all-white character (`cannot happen') */
row = CurRow;
col = CurCol >> 3;
i = height;
w = (width + 7) >> 3;
o = COLUMNS - w;
#if defined(lint) || !defined(vax)
rshift = CurCol & 7;
lshift = 8 - rshift;
#else lint || !vax
rshift = -(CurCol & 7); /* Vax does '>>' as negative '<<' */
lshift = 8 + rshift;
#endif lint || !vax
bp = &VBuffer[row][col];
#define avoiding_shifts_is_faster /* but is it??? */
#ifdef avoiding_shifts_is_faster
/*
* One out of eight or so times, the shift values will be
* zero. This makes the code run faster.
*/
if (rshift == 0) {
while (--i >= 0) {
j = w;
while (--j >= 0)
*bp++ |= *rp++;
if (++row >= ROWS) {
row = 0;
bp = &VBuffer[0][col];
} else
bp += o;
}
} else
#endif
{
while (--i >= 0) {
j = w;
while (--j >= 0) {
#if defined(lint) || !defined(vax)
*bp++ |= (*rp & 255) >> rshift;
*bp |= (*rp++ & 255) << lshift;
#else lint || !vax
/*
* THE FOLLOWING ASSEMBLY CODE IS INSERTED
* BECAUSE THE COMPILER CAN'T OPTIMIZE THE
* C CODE WORTH A DARN
*/
asm(" movzbl (r10)+,r1 # *rp++ & 255");
asm(" ashl r9,r1,r0 # >> rshift");
asm(" bisb2 r0,(r11)+ # *bp++ |=");
asm(" ashl r8,r1,r0 # << lshift");
asm(" bisb2 r0,(r11) # *bp |=");
#endif lint || !vax
}
if (++row >= ROWS) {
row = 0;
bp = &VBuffer[0][col];
} else
bp += o;
}
}
j = height + CurRow - 1;/* have now set bits this far */
if (j >= ROWS)
j -= ROWS; /* keep it modular */
/*
* There are two cases. Either the buffer is not currently wrapped,
* in which case the regions past LastRow or before FirstRow extend
* it; or it is wrapped, in which case the region between LastRow
* and FirstRow extends it:
*
* case 1 case 2
* -------- --------
* | | last ->| XXXX |
* first ->| XXXX | | |
* | XXXX | | |
* last ->| XXXX | first ->| XXXX |
* | | | XXXX |
* -------- --------
*
* The `X's mark the region that is in use; the blank spaces
* mark the region that causes the `last' value to change.
*/
if (FirstRow <= LastRow) {
/* first case: not wrapped */
if (j < FirstRow || j > LastRow)
LastRow = j;
} else {
/* second case: wrapped */
if (j > LastRow && j < FirstRow)
LastRow = j;
}
}
/*
* Write a rule at the current row according to the (packed) information in
* 'info'. This includes the x position and the height and width of the
* rule.
*/
VWriteRule(info)
int info;
{
register char *bp; /* buffer pointer */
register int j;
register int lbits; /* bits along left */
register int rbits; /* bits along right */
register int o; /* offset to next row */
register int i;
register int full; /* number of 8 bit words to set */
register int height; /* rule height */
register int width; /* rule width */
register int row;
register int col;
i = info;
CurCol = (i & 0x7fff0000) >> 16;
height = (i & 0xff00) >> 8;
width = i & 0xff;
col = CurCol >> 3;
row = CurRow;
j = CurCol & 7; /* bit # of start position */
lbits = 0xff >> j; /* bits to set along left edge */
/* there are 8-j bits set in lbits */
o = 8 - j - width;
if (o > 0) { /* then lbits has o too many bits set */
lbits >>= o;
lbits <<= o; /* puts zeros into o righthand bits */
rbits = 0;
full = 0;
} else {
i = (CurCol + width) & 7; /* bit # of ending position */
rbits = 0xff00 >> i; /* bits to set along right edge */
/* there are i bits set in rbits (well, in the low byte) */
full = (width - i - (8 - j)) >> 3;
}
bp = &VBuffer[row][col];
i = height;
/* Often "full" is zero, which makes things faster */
if (full) { /* oh well */
o = COLUMNS - full - 1;
while (--i >= 0) {
*bp++ |= lbits;
for (j = full; --j >= 0;)
*bp++ |= 0xff;
*bp |= rbits;
if (++row >= ROWS) {
row = 0;
bp = &VBuffer[0][col];
} else
bp += o;
}
} else {
o = COLUMNS - 1;
while (--i >= 0) {
*bp++ |= lbits;
*bp |= rbits;
if (++row >= ROWS) {
row = 0;
bp = &VBuffer[0][col];
} else
bp += o;
}
}
i = CurRow + height - 1;
if (i >= ROWS)
i -= ROWS;
/*
* This is another way of expressing both cases 1 and 2 in
* VWriteChar(). I think the other way is likely to be
* faster, and characters occur far more frequently; but this
* is the more readable by far.
*/
if (RowsBetween(FirstRow, LastRow) < RowsBetween(FirstRow, i))
LastRow = i;
}
/*
* Dump out the top portion of the band (rows [Firstrow, CurRow)).
*/
DumpTopOfBand()
{
/*
* To exclude CurRow, subtract one, but modularly, modularly!
*/
WriteBuf(&VBuffer[0][0], FirstRow, CurRow ? CurRow - 1 : ROWS - 1, 1);
FirstRow = CurRow;
}
/*
* Move the current row in the band buffer down by delta rows, by,
* if necessary, writing out the currently-used portion of the buffer.
*/
MoveDown(delta)
register int delta;
{
if (delta >= ROWS - RowsBetween(FirstRow, CurRow)) {
/*
* Need to roll the cylinder forward. Write out the used
* part, and then write as many blank lines as necessary.
*/
WriteBuf(&VBuffer[0][0], FirstRow, LastRow, 1);
WriteBlanks(delta - RowsBetween(CurRow, LastRow) - 1);
CurRow = LastRow = FirstRow; /* band is now empty */
} else {
/*
* Because RowsBetween returns nonnegative integers, we
* know delta <= ROWS, so can do mod more quickly thus:
*/
CurRow += delta; /* result < 2*ROWS */
if (CurRow >= ROWS)
CurRow -= ROWS; /* now result < ROWS */
}
}
/*
* Write the lines between the first and last inclusive from the given
* buffer. If 'cl', clear after writing.
*/
WriteBuf(buf, first, last, cl)
register char *buf;
register int first, last;
{
if (first > last) { /* recursively do wrapped part first */
WriteBuf(buf, first, ROWS - 1, cl);
first = 0;
}
buf = &buf[first * COLUMNS];
last = COLUMNS * (first = last - first + 1);
/*
* If the write fails, the Versatec is probably out of paper, and in
* any case, things are probably in bad shape.
*/
if (write(vp, buf, last) != last) {
error(0, errno, "Versatec write error");
longjmp(failbuf, 1);
}
if (cl)
bzero(buf, (unsigned) last);
NLines += first;
}
/*
* Write 'n' blank lines.
*/
WriteBlanks(n)
register int n;
{
register int k;
static char nullbuf[MIN_OUT][COLUMNS];
while (n > 0) {
k = n > MIN_OUT ? MIN_OUT : n;
WriteBuf(&nullbuf[0][0], 0, k - 1, 0);
n -= k;
}
}
/*
* Write cut marks. We borrow row 0 of VBuffer for this.
*/
CutMarks()
{
register short *bp = (short *) &VBuffer[0][0];
*bp = 0xffff;
bp[(COLUMNS / 2) - 1] = 0xffff;
WriteBuf(&VBuffer[0][0], 0, 0, 1);
}
/*
* Perform a page feed. Restore plot mode if `setplot'.
*/
FormFeed(setplot)
int setplot;
{
ioctl(vp, VSETSTATE, prtmd);
(void) write(vp, "\f", 2); /* \0 really IS required occasionally */
if (setplot)
ioctl(vp, VSETSTATE, pltmd);
}